﻿using Common.AspNetCore.Structs;
using Common.EF;
using Common.EF.Structs;
using Common.Infrastructure;
using Common.Infrastructure.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Common.AspNetCore
{
    public class CommonModelRepository : ICommonModelRepository
    {
        private readonly string default_model_id_api = "SysApi/GetIdModel";
        private readonly string default_model_api = "SysApi/GetModel";
        private readonly string defualt_model_order_api = "SysApi/GetOrderModel";
        private readonly string default_model_order_pgage_api = "SysApi/GetOrderPageModel";

        private readonly IServiceProvider provider;
        private readonly IServiceCallService callService;
        private readonly ICommonRepository repository;

        public CommonModelRepository(IServiceProvider provider, IServiceCallService serviceCall, ICommonRepository commonRepository)
        {
            this.provider = provider;
            this.callService = serviceCall;
            this.repository = commonRepository;
        }

        public IServiceProvider Provider
        {
            get { return provider; }
        }

        public async Task<T> GetModelAsync<T>(string id, string[] includes = null) 
            where T : ModelBase, new()
        {
            return string.IsNullOrEmpty(id) ? null : await this.DoGetModel<T>(id, null, includes);
        }

        public async Task<T> GetModelAsync<T>(ApiParam apiParam, string[] includes = null)
            where T : ModelBase, new()
        {
            return apiParam == null ? null : await this.DoGetModel<T>(null, apiParam, includes);
        }

        public async Task<List<T>> GetModelsAsync<T>(List<string> ids, string[] includes = null) 
            where T : ModelBase, new()
        {
            return ids == null || !ids.Any() ? new List<T>() : await DoGetModels<T>(ids, null, false, includes);
        }

        public async Task<List<T>> GetModelsAsync<T>(ApiParam apiParam, string[] includes = null)
            where T : ModelBase, new()
        {
            return apiParam == null ? new List<T>() : await DoGetModels<T>(null, apiParam, false, includes);
        }

        public async Task<List<T>> GetOrderModelsAsync<T>(ApiParam apiParam, string[] includes = null)
            where T : ModelBase, new()
        {
            return apiParam == null ? new List<T>() : await DoGetModels<T>(null, apiParam, true, includes);
        }

        public async Task<ApiPagedResult<T>> GetOrderPageModelsAsync<T>(ApiOrderPageParam apiOrderPageParam, string[] includes = null)
          where T : ModelBase, new()
        {
            return apiOrderPageParam == null
                ? new ApiPagedResult<T>() { Data = new List<T>() }
                : await DoGetOrderPageModels<T>(apiOrderPageParam, includes);
        }

        public object GetModelByIdByReqest(ModelIdRequest model)
        {
            this.CheckModelRequest(model);
            LibCheckNullUtils.CheckNull(model.Ids, "model.Ids");  
            if (!model.Ids.Any())
            {
                throw new ArgumentNullException("model.Ids");
            }

            var type = GetEntityType(model.ModelName);
            if (type != null && (model.Includes == null || !model.Includes.Any()))
            {
                var methodInfo = this.GetType().GetMethod("GetEntityObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                return methodInfo.MakeGenericMethod(new Type[] { type }).Invoke(this, new object[] { model.Ids, null, false, null });
            }

            var modelType = ModelTypeManager.GetModelTypeByName(model.ModelName);
            if (modelType == null)
            {
                throw new CommonException($"Model Type {model.ModelName} is null");
            }

            var modelObjInfo = this.GetType().GetMethod("GetModelObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
            return modelObjInfo.MakeGenericMethod(new Type[] { modelType.Type }).Invoke(this, new object[] { model.Ids, model.Includes.ToArray(), null, false, null });
        }

        public object GetModelByRequest(ModelRequest model)
        {
            this.CheckModelRequest(model);
            LibCheckNullUtils.CheckNull(model.ApiParam, "model.ApiParam"); 
            var type = GetEntityType(model.ModelName);
            bool hasIncludeField = false;
            CheckGroupField(model.ApiParam.Group, ref hasIncludeField);
            if (type != null && (model.ApiParam.Includes == null || !model.ApiParam.Includes.Any()) && !hasIncludeField)
            {
                var methodInfo = this.GetType().GetMethod("GetEntityObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                return methodInfo.MakeGenericMethod(new Type[] { type }).Invoke(this, new object[] { null, model.ApiParam, false, null });
            }

            var modelType = ModelTypeManager.GetModelTypeByName(model.ModelName);
            if (modelType == null)
            {
                throw new CommonException($"Model Type {model.ModelName} is null");
            }

            var modelObjInfo = this.GetType().GetMethod("GetModelObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
            return modelObjInfo.MakeGenericMethod(new Type[] { modelType.Type }).Invoke(this, new object[] { null, null, model.ApiParam, false, null });
        }

        public object GetOrderModelByRequest(ModelRequest model)
        {
            this.CheckModelRequest(model);
            LibCheckNullUtils.CheckNull(model.ApiParam, "model.ApiParam"); 
            var type = GetEntityType(model.ModelName);
            bool hasIncludeField = false;
            CheckGroupField(model.ApiParam.Group, ref hasIncludeField);
            if (type != null && (model.ApiParam.Includes == null || !model.ApiParam.Includes.Any()) && !hasIncludeField)
            {
                var methodInfo = this.GetType().GetMethod("GetEntityObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                return methodInfo.MakeGenericMethod(new Type[] { type }).Invoke(this, new object[] { null, model.ApiParam, true, null });
            }

            var modelType = ModelTypeManager.GetModelTypeByName(model.ModelName);
            if (modelType == null)
            {
                throw new CommonException($"Model Type {model.ModelName} is null");
            }

            var modelObjInfo = this.GetType().GetMethod("GetModelObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
            return modelObjInfo.MakeGenericMethod(new Type[] { modelType.Type }).Invoke(this, new object[] { null, null, model.ApiParam, true, null });
        }

        public object GetOrderPageModelByRequest(ModelOrderPageRequest model)
        {
            this.CheckModelRequest(model);
            LibCheckNullUtils.CheckNull(model.OrderPageParam, "model.ApiParam"); 
            var type = GetEntityType(model.ModelName);
            bool hasIncludeField = false;
            CheckGroupField(model.OrderPageParam.Group, ref hasIncludeField);
            if (type != null &&
                (model.OrderPageParam.Includes == null ||
                 !model.OrderPageParam.Includes.Any()) &&
                 !hasIncludeField)
            {
                var methodInfo = this.GetType().GetMethod("GetEntityObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                return methodInfo.MakeGenericMethod(new Type[] { type }).Invoke(this, new object[] { null, null, false, model.OrderPageParam });
            }

            var modelType = ModelTypeManager.GetModelTypeByName(model.ModelName);
            if (modelType == null)
            {
                throw new CommonException($"Model Type {model.ModelName} is null");
            }

            var modelObjInfo = this.GetType().GetMethod("GetModelObjectByRequest", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
            return modelObjInfo.MakeGenericMethod(new Type[] { modelType.Type }).Invoke(this, new object[] { null, null, null, false, model.OrderPageParam });
        }

        public async Task SaveModelsAsync(ModelSaveRequest request)
        {
            if (request.Details == null || !request.Details.Any())
            {
                return;
            }

            await this.repository.CommitChangesAsync(() =>
            {
                foreach (var s in request.Details)
                {
                    var modelName = s.ModelName;
                    LibCheckNullUtils.CheckNull(s.ModelName, "ModelName");
                    if (modelName.StartsWith("m_"))
                    {
                        modelName = "t_" + modelName.Substring(2);
                    }

                    var t1 = EntityTypeManager.GetEntityType(modelName);
                    if (t1 == null)
                    {
                        throw new CommonException($"Entity Type {modelName} is null");
                    }

                    var methodInfo = this.GetType().GetMethod("SaveModelByInvoke", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                    methodInfo.MakeGenericMethod(new Type[] { t1.Type }).Invoke(this, new object[] { s });
                }
            });
        }

        public async Task DeleteModelsAsync(ModelDeleteRequest request)
        {
            if (request.Details == null || !request.Details.Any())
            {
                return;
            }

            await this.repository.CommitChangesAsync(() =>
            {
                foreach (var s in request.Details)
                {
                    var modelName = s.ModelName;
                    LibCheckNullUtils.CheckNull(s.ModelName, "ModelName");
                    if (modelName.StartsWith("m_"))
                    {
                        modelName = "t_" + modelName.Substring(2);
                    }

                    var t1 = EntityTypeManager.GetEntityType(modelName);
                    if (t1 == null)
                    {
                        throw new CommonException($"Entity Type {modelName} is null");
                    }

                    var methodInfo = this.GetType().GetMethod("DeleteModelByInvoke", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                    methodInfo.MakeGenericMethod(new Type[] { t1.Type }).Invoke(this, new object[] { s });
                }
            });
        }

        public List<string> GetIdsByEntity(IDParam param)
        {
            if (param.EntityName.StartsWith("m_"))
            {
                param.EntityName = "t_" + param.EntityName.Substring(2);
            }

            return this.repository.GetEntityIds(param);
        }

        private async Task<T> DoGetModel<T>(string id, ApiParam apiParam, string[] includes = null) where T : ModelBase, new()
        {
            string modelName = typeof(T).Name;
            var modelConfig = ModelConfigManager.GetModelConfig(modelName);
            LibCheckNullUtils.CheckNull(modelConfig, "ModelConfig");
            LibCheckNullUtils.CheckNull(modelConfig.ModuleCode, "modelConfig.ModuleCode");
            LibCheckNullUtils.CheckNull(modelConfig.EntityName, "modelConfig.EntityName");
            var entityType = EntityTypeManager.GetEntityType(modelConfig.EntityName);
            await SetApiGroupParam(apiParam, modelConfig);
            var tempincludes = GetIncludes(apiParam, includes);
            if (apiParam != null && apiParam.Includes != null)
            {
                apiParam.Includes.Clear();
            }

            T t = null;
            if (entityType != null)
            {
                LibCheckNullUtils.CheckNull(modelConfig.EntityName, "EntityName");
                t = await DoGetEntityByModelAsync<T>(id, apiParam, modelConfig);
            }
            else
            {
                string url = !string.IsNullOrEmpty(modelConfig.ModelIdApi) ? modelConfig.ModelIdApi : default_model_id_api;
                ModelRequestBase modeR;
                if (!string.IsNullOrEmpty(id))
                {
                    var mode = new ModelIdRequest();
                    mode.Ids.Add(id);
                    modeR = mode;
                }
                else
                {
                    var mode = new ModelRequest();
                    mode.ApiParam = apiParam;
                    modeR = mode;
                }

                modeR.ModelName = modelConfig.Name;
                modeR.ServiceCode = modelConfig.ModuleCode;
                var ret = await this.callService.PostListAsync<T>(modelConfig.ModuleCode, url, modeR);
                if (ret != null)
                {
                    t = ret.FirstOrDefault();
                }
            }

            if (t != null)
            {
                List<T> list = new List<T>
                {
                    t
                };
                await FillIncludeByModelAsync(list, tempincludes, modelConfig);
            }

            return t;
        }

        private async Task<List<T>> DoGetModels<T>(List<string> ids, ApiParam apiParam, bool isOrder, string[] includes = null) where T : ModelBase, new()
        {
            string modelName = typeof(T).Name;
            var modelConfig = ModelConfigManager.GetModelConfig(modelName);
            LibCheckNullUtils.CheckNull(modelConfig, "ModelConfig");
            LibCheckNullUtils.CheckNull(modelConfig.ModuleCode, "ModuleCode");
            LibCheckNullUtils.CheckNull(modelConfig.EntityName, "modelConfig.EntityName");
            var entityType = EntityTypeManager.GetEntityType(modelConfig.EntityName);
            await SetApiGroupParam(apiParam, modelConfig);
            List<T> t;
            var tempincludes = GetIncludes(apiParam, includes);
            if (apiParam != null && apiParam.Includes != null)
            {
                apiParam.Includes.Clear();
            }

            if (entityType != null)
            {
                LibCheckNullUtils.CheckNull(modelConfig.EntityName, "EntityName");
                t = await DoGetEntitysByModelAsync<T>(ids, apiParam, isOrder, modelConfig);
            }
            else
            {
                string url;
                ModelRequestBase modelRequestBase;
                if (isOrder)
                {
                    modelRequestBase = new ModelRequest()
                    {
                        ApiParam = apiParam,
                    };
                    url = !string.IsNullOrEmpty(modelConfig.ModelOrderApi) ? modelConfig.ModelOrderApi : defualt_model_order_api;
                }
                else
                {
                    if (ids != null && ids.Any())
                    {
                        url = !string.IsNullOrEmpty(modelConfig.ModelIdApi) ? modelConfig.ModelIdApi : default_model_id_api;
                        var modelId = new ModelIdRequest();
                        modelId.Ids.AddRange(ids);
                        modelRequestBase = modelId;
                    }
                    else
                    {
                        url = !string.IsNullOrEmpty(modelConfig.ModelApi) ? modelConfig.ModelApi : default_model_api;
                        modelRequestBase = new ModelRequest()
                        {
                            ApiParam = apiParam
                        };
                    }
                }

                modelRequestBase.ModelName = modelName;
                modelRequestBase.ServiceCode = modelConfig.ModuleCode;
                t = await this.callService.PostListAsync<T>(modelConfig.ModuleCode, url, modelRequestBase);
            }

            if (t != null && t.Any())
            {
                await FillIncludeByModelAsync(t.ToList(), tempincludes, modelConfig);
            }

            return t;
        }

        private async Task<ApiPagedResult<T>> DoGetOrderPageModels<T>(ApiOrderPageParam apiOrderPageParam, string[] includes = null)
         where T : ModelBase, new()
        {
            string modelName = typeof(T).Name;
            var modelConfig = ModelConfigManager.GetModelConfig(modelName);
            LibCheckNullUtils.CheckNull(modelConfig, "ModelConfig");
            LibCheckNullUtils.CheckNull(modelConfig.ModuleCode, "ModuleCode");
            LibCheckNullUtils.CheckNull(modelConfig.EntityName, "modelConfig.EntityName");
            var entityType = EntityTypeManager.GetEntityType(modelConfig.EntityName);
            await SetApiGroupParam(apiOrderPageParam, modelConfig);
            var tempincludes = GetIncludes(apiOrderPageParam, includes);
            if (apiOrderPageParam != null && apiOrderPageParam.Includes != null)
            {
                apiOrderPageParam.Includes.Clear();
            }

            ApiPagedResult<T> t;
            if (entityType != null)
            {
                LibCheckNullUtils.CheckNull(modelConfig.EntityName, "EntityName");
                t = await DoGetOrderPageEntitysByModelAsync<T>(apiOrderPageParam, modelConfig);
            }
            else
            {
                string url = !string.IsNullOrEmpty(modelConfig.ModelOrderPageApi) ? modelConfig.ModelOrderPageApi : default_model_order_pgage_api;
                ModelOrderPageRequest modelRequest = new ModelOrderPageRequest
                {
                    OrderPageParam = apiOrderPageParam,
                    ModelName = modelName,
                    ServiceCode = modelConfig.ModuleCode,
                };
                t = await this.callService.PostApiPageAsync<T>(modelConfig.ModuleCode, url, modelRequest);
            }

            if (t != null && t.Data != null && t.Data.Any())
            {
                await FillIncludeByModelAsync(t.Data.ToList(), tempincludes, modelConfig);
            }

            return t;
        }

        private string[] GetIncludes(ApiParam apiParam, string[] includes)
        {
            List<string> ret = new List<string>();
            if (apiParam != null && apiParam.Includes != null && apiParam.Includes.Any())
            {
                ret.AddRange(apiParam.Includes);
            }

            if (includes != null && includes.Any())
            {
                ret.AddRange(includes);
            }

            return ret.ToArray();
        }

        private Task<T> DoGetEntityByModelAsync<T>(string id, ApiParam apiParam, ModelConfig modelConfig) where T : ModelBase, new()
        {
            return Task.Run(() =>
            {
                LibCheckNullUtils.CheckNull(modelConfig.EntityName, "EntityName");
                var entityType = EntityTypeManager.GetEntityType(modelConfig.EntityName);
                LibCheckNullUtils.CheckNull(entityType, "EntityName");
                MethodInfo methodInfo = this.GetType().GetMethod("DoGetEntityByModel", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                var obj = methodInfo.MakeGenericMethod(new Type[] { entityType.Type }).Invoke(this, new object[] { id, apiParam });
                var t = this.DoGetT<T>(obj);
                return t;
            });
        }

        private T DoGetEntityByModel<T>(string id, ApiParam apiParam) where T : EntityBase, new()
        {
            if (apiParam == null && string.IsNullOrEmpty(id))
            {
                LibCheckNullUtils.CheckNull(apiParam, "ApiParam");
            }

            return apiParam != null ? this.repository.GetEntity<T>(apiParam, null) : this.repository.GetEntity<T>(id);
        }

        private async Task SetApiGroupParam(ApiParam apiParam, ModelConfig model)
        {
            if (apiParam == null)
            {
                return;
            }

            await DoSetApiGroupParam(apiParam, model, apiParam.Group);
        }

        private async Task DoSetApiGroupParam(ApiParam apiParam, ModelConfig model, ApiGroupParam groupParam)
        {
            if (groupParam == null)
            {
                return;
            }

            List<ApiGroupParam> groups = new List<ApiGroupParam>();
            List<ApiRuleParam> removeRules = new List<ApiRuleParam>();
            List<FilterGroupModel> groupModels = new List<FilterGroupModel>();
            if (groupParam.Rules != null && groupParam.Rules.Any())
            {
                foreach (var r in groupParam.Rules)
                {
                    var i1 = r.Field.IndexOf('[');
                    var i = r.Field.IndexOf('.');
                    if (i1 >= 0)
                    {
                        string s = r.Field.Substring(0, i1);
                        var i2 = r.Field.IndexOf(']');
                        string s1 = r.Field.Substring(i1 + 1, i2 - i1 - 1);
                        var f = model.Fields.FirstOrDefault(t => string.Compare(t.Name, s, true) == 0);
                        if (f == null)
                        {
                            LibMessageUtils.ThrowError(this.Provider, "100016", model.Name, s); // throw new Exception("api filter param error:" + r.Field);
                        }

                        if (f is ModelManyNavFieldConfig)
                        {
                            var navConfig = f as ModelManyNavFieldConfig;
                            var gm = groupModels.FirstOrDefault(t => t.NavName == navConfig.Name);
                            if (gm == null)
                            {
                                gm = new FilterGroupModel
                                {
                                    NavName = navConfig.Name,
                                    IdName = navConfig.IdField,
                                    FilterName = "id",
                                    IsAnd = groupParam.IsAnd,
                                    NavModelName = navConfig.NavModelName
                                };
                                groupModels.Add(gm);
                            }

                            FilterGroupRuleModel gr = new FilterGroupRuleModel
                            {
                                Field = s1,
                                Operate = r.Operate,
                                Value = r.Value
                            };
                            gm.Rules.Add(gr);
                            removeRules.Add(r);
                        }
                        else
                        {
                            LibMessageUtils.ThrowError(this.Provider, "100015", model.Name, s);
                        }
                    }
                    else if (i >= 0)
                    {
                        string s = r.Field.Substring(0, i);
                        var f = model.Fields.FirstOrDefault(t => string.Compare(t.Name, s, true) == 0);
                        if (f == null)
                        {
                            LibMessageUtils.ThrowError(this.Provider, "100016", model.Name, s);
                        }

                        if (f is ModelNavFieldConfig)
                        {
                            var navConfig = f as ModelNavFieldConfig;
                            string s1 = r.Field.Substring(i + 1);

                            var gm = groupModels.FirstOrDefault(t => t.NavName == navConfig.Name);
                            if (gm == null)
                            {
                                gm = new FilterGroupModel
                                {
                                    NavName = navConfig.Name,
                                    IdName = "id",
                                    FilterName = navConfig.IdField,
                                    IsAnd = groupParam.IsAnd,
                                    NavModelName = navConfig.NavModelName
                                };
                                groupModels.Add(gm);
                            }

                            FilterGroupRuleModel gr = new FilterGroupRuleModel
                            {
                                Field = s1,
                                Operate = r.Operate,
                                Value = r.Value
                            };
                            gm.Rules.Add(gr);
                            removeRules.Add(r);
                        }
                        else
                        {
                            LibMessageUtils.ThrowError(this.Provider, "100015", model.Name, s);
                        }
                    }
                }
            }

            foreach (var g in groupParam.Groups)
            {
                await DoSetApiGroupParam(apiParam, model, g);
            }

            foreach (var i in groupModels)
            {
                ApiParam p = new ApiParam();
                p.Group.IsAnd = i.IsAnd;
                foreach (var r in i.Rules)
                {
                    p.Group.Rules.Add(new ApiRuleParam() { Field = r.Field, Operate = r.Operate, Value = r.Value });
                }

                p.OrgId = apiParam.OrgId;
                var ret = await GetModelByApiParamAsync(i.NavModelName, p);
                List<string> ids = new List<string>();
                ApiGroupParam g = new ApiGroupParam
                {
                    IsAnd = false
                };
                if (ret != null && ret.Any())
                {
                    foreach (var e in ret)
                    {
                        var p1 = e.GetType().GetProperty(i.IdName);
                        if (p1 != null)
                        {
                            var pv1 = p1.GetValue(e, null);
                            if (pv1 != null)
                            {
                                ids.Add(pv1.ToString());
                            }
                        }
                    }
                }

                if (!ids.Any())
                {
                    ids.Add("xxx");
                }

                ids = ids.Distinct().ToList();
                ids.ForEach(t =>
                {
                    g.Rules.Add(new ApiRuleParam() { Field = i.FilterName, Operate = ApiRuleOperateEnum.等于, Value = t });
                });
                groups.Add(g);
            }

            removeRules.ForEach(t => groupParam.Rules.Remove(t));
            if (groups.Any())
            {
                groupParam.Groups.AddRange(groups);
            }
        }

        private Task<List<T>> DoGetEntitysByModelAsync<T>(List<string> ids, ApiParam apiParam, bool isOrder, ModelConfig modelConfig) where T : ModelBase, new()
        {
            return Task.Run(() =>
            {
                List<T> list = new List<T>();
                LibCheckNullUtils.CheckNull(modelConfig.EntityName, "EntityName");
                var entityType = EntityTypeManager.GetEntityType(modelConfig.EntityName);
                LibCheckNullUtils.CheckNull(entityType, "entityType");
                MethodInfo methodInfo = this.GetType().GetMethod("DoGetEntitysByModel", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                var obj = methodInfo.MakeGenericMethod(new Type[] { entityType.Type }).Invoke(this, new object[] { ids, apiParam, isOrder });
                if (obj != null && obj is System.Collections.IList)
                {
                    System.Collections.IList enumerator = obj as System.Collections.IList;
                    for (int i = 0; i < enumerator.Count; i++)
                    {
                        var ix = enumerator[i];
                        var t = this.DoGetT<T>(ix);
                        list.Add(t);
                    }
                }

                return list;
            });
        }

        private List<T> DoGetEntitysByModel<T>(List<string> ids, ApiParam apiParam, bool isOrder) where T : EntityBase, new()
        {
            if (apiParam == null && ids == null)
            {
                LibCheckNullUtils.CheckNull(apiParam, "ApiParam");
            }

            if (isOrder)
            {
                if (apiParam == null)
                {
                    LibCheckNullUtils.CheckNull(null, "ApiParam");
                }

                return this.repository.GetOrderEntities<T>(apiParam, t => true);
            }

            return apiParam != null ? this.repository.GetEntities<T>(apiParam, t => true) : this.repository.GetEntities<T>(ids);
        } 

        private Task<ApiPagedResult<T>> DoGetOrderPageEntitysByModelAsync<T>(ApiOrderPageParam apiOrderPageParam, ModelConfig modelConfig)
              where T : ModelBase, new()
        {
            return Task.Run(() =>
            {
                ApiPagedResult<T> ret = new ApiPagedResult<T>();
                PageResultDtoByMode mode = new PageResultDtoByMode();
                List<T> list = new List<T>();
                LibCheckNullUtils.CheckNull(modelConfig.EntityName, "EntityName");
                var entityType = EntityTypeManager.GetEntityType(modelConfig.EntityName);
                LibCheckNullUtils.CheckNull(entityType, "entityType");
                MethodInfo methodInfo = this.GetType().GetMethod("DoGetOrderPageEntitysByModel", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                var obj = methodInfo.MakeGenericMethod(new Type[] { entityType.Type }).Invoke(this, new object[] { apiOrderPageParam, mode });
                ret.TotalCount = mode.TotalCount;
                if (obj != null && obj is System.Collections.IList)
                {
                    System.Collections.IList enumerator = obj as System.Collections.IList;
                    for (int i = 0; i < enumerator.Count; i++)
                    {
                        var ix = enumerator[i];
                        var t = this.DoGetT<T>(ix);
                        list.Add(t);
                    }
                }

                ret.Data = list;
                return ret;
            });
        }

        private List<T> DoGetOrderPageEntitysByModel<T>(ApiOrderPageParam apiOrderPageParam, PageResultDtoByMode mode) where T : EntityBase, new()
        {
            var ret = this.repository.GetOrderPageEntities<T>(apiOrderPageParam, null);
            if (ret != null)
            {
                mode.TotalCount = ret.TotalCount;
                return ret.Data;
            }

            return null;
        }

        private async Task FillIncludeByModelAsync<T>(List<T> list, string[] includes, ModelConfig modelConfig) where T : ModelBase, new()
        {
            if (includes == null || !includes.Any())
            {
                return;
            }

            includes = includes.Distinct().ToArray();
            List<FillIncludeModel> models = new List<FillIncludeModel>();
            string typeName = typeof(T).Name;
            foreach (var c in includes)
            {
                var c1 = c.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                if (c1 != null && c1.Any())
                {
                    var nav = c1[0];
                    var field = modelConfig.Fields.FirstOrDefault(t => string.Compare(t.Name, nav, true) == 0);
                    if (field == null)
                    {
                        LibMessageUtils.ThrowError(this.Provider, "100014", modelConfig.Name, nav);
                    }

                    if (field is ModelNavFieldConfig || field is ModelManyNavFieldConfig)
                    {
                        FillIncludeModel fillIncludeModel = models.FirstOrDefault(t => string.Compare(t.NavName, field.Name, true) == 0);
                        if (fillIncludeModel == null)
                        {
                            fillIncludeModel = new FillIncludeModel
                            {
                                NavName = field.Name
                            };
                            models.Add(fillIncludeModel);
                        }

                        if (field is ModelNavFieldConfig)
                        {
                            var fs = field as ModelNavFieldConfig;
                            LibCheckNullUtils.CheckNull(fs.IdField, typeName + " " + nav + " IdField");
                            LibCheckNullUtils.CheckNull(fs.NavModelName, typeName + " " + nav + " NavModelName");
                            fillIncludeModel.FilterName = "id";
                            fillIncludeModel.SrcName = fs.IdField;
                            fillIncludeModel.ModelName = fs.NavModelName;
                            fillIncludeModel.IsOne = true;
                        }
                        else if (field is ModelManyNavFieldConfig)
                        {
                            var fs = field as ModelManyNavFieldConfig;
                            LibCheckNullUtils.CheckNull(fs.IdField, typeName + " " + nav + " IdField");
                            LibCheckNullUtils.CheckNull(fs.NavModelName, typeName + " " + nav + " NavModelName");
                            fillIncludeModel.FilterName = fs.IdField;
                            fillIncludeModel.SrcName = "id";
                            fillIncludeModel.ModelName = fs.NavModelName;
                        }
                        else
                        {
                            LibMessageUtils.ThrowError(this.Provider, "100015", modelConfig.Name, nav);
                        }

                        if (c1.Length > 1)
                        {
                            var t = c.Substring(c.IndexOf(nav + ".") + nav.Length + 1);
                            if (!fillIncludeModel.Navs.Contains(t))
                            {
                                fillIncludeModel.Navs.Add(t);
                            }
                        }
                    }
                }
            }

            if (!models.Any())
            {
                return;
            }

            foreach (var p in list)
            {
                foreach (var m in models)
                {
                    var p1 = p.GetType().GetProperty(m.SrcName);
                    if (p1 != null)
                    {
                        var v = p1.GetValue(p, null);
                        if (v != null && !m.Ids.Contains(v.ToString()))
                        {
                            m.Ids.Add(v.ToString());
                        }

                        if (m.BeginCreateTime == null)
                        {
                            m.BeginCreateTime = p.create_time;
                        }
                        else if (m.BeginCreateTime > p.create_time)
                        {
                            m.BeginCreateTime = p.create_time;
                        }
                    }
                }
            }

            await GetModelByIncludesAsync(models);

            foreach (var p in list)
            {
                foreach (var m in models)
                {
                    if (m.Ids.Any())
                    {
                        var t1 = ModelTypeManager.GetModelTypeByName(m.ModelName);
                        MethodInfo methodInfo = this.GetType().GetMethod("DoFillModel", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                        methodInfo.MakeGenericMethod(new Type[] { t1.Type }).Invoke(this, new object[] { m, p });
                    }
                }
            }
        }

        private async Task GetModelByIncludesAsync(List<FillIncludeModel> models)
        {
            models = models.Where(t => t.Ids != null && t.Ids.Any()).ToList();
            List<FillIncludeModel> otherModuleModels = new List<FillIncludeModel>();
            foreach (var item in models)
            {
                var config = ModelConfigManager.GetModelConfig(item.ModelName);
                var entityType = config != null ? EntityTypeManager.GetEntityType(config.EntityName) : null;
                if (config != null && entityType != null)
                {
                    await GetModelByIncludeAsync(item);
                }
                else
                {
                    otherModuleModels.Add(item);
                }
            }

            List<Task> tasks = new List<Task>();
            foreach (var xx in otherModuleModels)
            {
                var t = GetModelByIncludeAsync(xx);
                tasks.Add(t);
            }

            var process = tasks.Select(async tt =>
            {
                await tt;
            });
            await Task.WhenAll(process);
        }

        private Task GetModelByIncludeAsync(FillIncludeModel fill)
        {
            return Task.Run(() =>
            {
                var t1 = ModelTypeManager.GetModelTypeByName(fill.ModelName);
                MethodInfo methodInfo = this.GetType().GetMethod("DoGetModelByIncludes", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                methodInfo.MakeGenericMethod(new Type[] { t1.Type }).Invoke(this, new object[] { fill });
            });
        }

        private void DoGetModelByIncludes<T>(FillIncludeModel fill) where T : ModelBase, new()
        {
            List<T> obj = null;
            if (string.Compare(fill.FilterName, "id", true) == 0)
            {
                obj = this.GetModelsAsync<T>(fill.Ids, fill.Navs.ToArray()).Result;
            }
            else
            {
                ApiParam apiParam = new ApiParam
                {
                    BeginTime = fill.BeginCreateTime
                };
                apiParam.Group.Rules.Add(new ApiRuleParam()
                {
                    Field = fill.FilterName,
                    Operate = ApiRuleOperateEnum.InByString,
                    Value = string.Join(",", fill.Ids)
                });
                obj = this.GetModelsAsync<T>(apiParam, fill.Navs.ToArray()).Result;
            }

            List<ModelBase> list = new List<ModelBase>();
            if (obj != null && obj is System.Collections.IList)
            {
                System.Collections.IList enumerator = obj as System.Collections.IList;
                for (int i = 0; i < enumerator.Count; i++)
                {
                    var ix = enumerator[i];
                    list.Add(ix as T);
                }
            }

            fill.Obj = list;
        }

        private Task<List<ModelBase>> GetModelByApiParamAsync(string modelName, ApiParam apiParam)
        {
            return Task.Run(() =>
            {
                var t1 = ModelTypeManager.GetModelTypeByName(modelName);
                MethodInfo methodInfo = this.GetType().GetMethod("DoGetModelByApiParam", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
                var obj = methodInfo.MakeGenericMethod(new Type[] { t1.Type }).Invoke(this, new object[] { apiParam });
                return obj as List<ModelBase>;
            });
        }

        private List<ModelBase> DoGetModelByApiParam<T>(ApiParam apiParam) where T : ModelBase, new()
        {
            List<T> obj = this.GetModelsAsync<T>(apiParam).Result;
            List<ModelBase> list = new List<ModelBase>();
            if (obj != null && obj is System.Collections.IList)
            {
                System.Collections.IList enumerator = obj as System.Collections.IList;
                for (int i = 0; i < enumerator.Count; i++)
                {
                    var ix = enumerator[i];
                    list.Add(ix as T);
                }
            }

            return list;
        }

        private void DoFillModel<T>(FillIncludeModel fill, object obj) where T : ModelBase, new()
        {
            var v = obj.GetType().GetProperty(fill.SrcName).GetValue(obj);
            if (v == null || string.IsNullOrEmpty(v.ToString()))
            {
                return;
            }

            ApiParam filter = new ApiParam();
            filter.Group.Rules.Add(new ApiRuleParam()
            {
                Field = fill.FilterName,
                Operate = ApiRuleOperateEnum.等于,
                Value = LibSysUtils.ToString(v)
            });
            Expression<Func<T, bool>> ext = filter.Group.GetExpression<T>();
            List<T> list = new List<T>();
            foreach (var x in fill.Obj)
            {
                list.Add(x as T);
            }

            var xx = list.AsQueryable().Where(ext).ToList();
            var p1 = obj.GetType().GetProperty(fill.NavName);
            if (fill.IsOne)
            {
                var yy = xx.FirstOrDefault();
                if (yy != null)
                {
                    p1.SetValue(obj, yy);
                }
            }
            else
            {
                p1.SetValue(obj, xx.ToList());
            }
        }

        private T DoGetT<T>(object obj) where T : ModelBase, new()
        {
            if (obj == null)
            {
                return null;
            }

            var t = new T();
            foreach (var p in t.GetType().GetProperties())
            {
                var p1 = obj.GetType().GetProperty(p.Name);
                if (p1 != null)
                {
                    p.SetValue(t, p1.GetValue(obj));
                }
            }

            return t;
        }

        private Type GetEntityType(string modelName)
        {
            if (modelName.StartsWith("m_"))
            {
                modelName = "t_" + modelName.Substring(2);
            }

            var t1 = EntityTypeManager.GetEntityType(modelName);
            return t1?.Type;
        }

        private void CheckGroupField(ApiGroupParam groupParam, ref bool hasIncludeField)
        {
            if (groupParam == null)
            {
                return;
            }

            if (groupParam.Rules.Any(t => t.Field.Contains(".")))
            {
                hasIncludeField = true;
                return;
            }

            foreach (var g in groupParam.Groups)
            {
                CheckGroupField(g, ref hasIncludeField);
            }
        }

        private void SaveModelByInvoke<T>(ModelSaveDetailRequest request) where T : EntityBase, new()
        {
            if (string.IsNullOrEmpty(request.ModelData))
            {
                return;
            }

            var s = request.ModelData.ToObjectFromJson<List<T>>();
            s.ForEach(t =>
            {
                if (string.IsNullOrEmpty(t.id))
                {
                    var ids = this.repository.GetEntityIds(new IDParam() { EntityName = typeof(T).Name, Count = 1 });
                    t.id = ids[0];
                }
            });
            this.repository.SaveEntities(s);
        }

        private void DeleteModelByInvoke<T>(ModelDeleteDetailRequest request) where T : EntityBase, new()
        {
            if (request.Ids == null || !request.Ids.Any())
            {
                return;
            }

            this.repository.DeleteEntity<T>(request.Ids);
        }

        private object GetEntityObjectByRequest<T>(List<string> ids, ApiParam apiParam, bool isOrder, ApiOrderPageParam apiOrderPageParam)
                where T : EntityBase, new()
        {
            if (ids != null && ids.Any())
            {
                return this.repository.GetEntities<T>(ids);
            }
            else if (apiParam != null)
            {
                if (isOrder)
                {
                    return this.repository.GetOrderEntities<T>(apiParam);
                }

                return this.repository.GetEntities<T>(apiParam);
            }
            else if (apiOrderPageParam != null)
            {
                return this.repository.GetOrderPageEntities<T>(apiOrderPageParam);
            }

            return null;
        }

        private object GetModelObjectByRequest<T>(List<string> ids, string[] includes, ApiParam apiParam, bool isOrder, ApiOrderPageParam apiOrderPageParam)
                where T : ModelBase, new()
        {
            if (ids != null && ids.Any())
            {
                return this.GetModelsAsync<T>(ids, includes).Result;
            }
            else if (apiParam != null)
            {
                if (isOrder)
                {
                    return this.GetOrderModelsAsync<T>(apiParam).Result;
                }

                return this.GetModelsAsync<T>(apiParam).Result;
            }
            else if (apiOrderPageParam != null)
            {
                return this.GetOrderPageModelsAsync<T>(apiOrderPageParam).Result;
            }

            return null;
        }

        private void CheckModelRequest(ModelRequestBase model)
        {
            LibCheckNullUtils.CheckNull(model, "model");
            LibCheckNullUtils.CheckNull(model.ModelName, "Model.ModelName");
        }

        private class PageResultDtoByMode
        {
            public int TotalCount { get; set; }
        }

        private class FillIncludeModel
        {
            public FillIncludeModel()
            {
                this.Ids = new List<string>();
                this.Navs = new List<string>();
            }

            public string ModelName { get; set; }

            public string FilterName { get; set; }

            public string SrcName { get; set; }

            public string NavName { get; set; }

            public bool IsOne { get; set; }

            public List<string> Navs { get; set; }

            public List<string> Ids { get; set; }

            public DateTime? BeginCreateTime { get; set; }

            public List<ModelBase> Obj { get; set; }
        }

        private class FilterGroupModel
        {
            public FilterGroupModel()
            {
                this.Rules = new List<FilterGroupRuleModel>();
            }

            public string NavName { get; set; }

            public string NavModelName { get; set; }

            public string IdName { get; set; }

            public bool IsAnd { get; set; }

            public string FilterName { get; set; }

            public List<FilterGroupRuleModel> Rules { get; set; }
        }

        private class FilterGroupRuleModel
        {
            public string Field { get; set; }

            public ApiRuleOperateEnum Operate { get; set; }

            public string Value { get; set; }
        }
    }
}
